package cn.iocoder.springboot.lab23.testdemo.service;

import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RBloomFilter;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

@Slf4j
@Component
public class BloomFilterTool {
    private static String BloomFilterKeyPrefix = "BloomFilter:";

    private static final long SCHEDULER_PERIOD = 20 * 1000L;

    private static final long expectedInsertions = 10000000L;

    private static final double falseProbability = 0.0001;
    @Getter
    private volatile static String lastBloomFilterKey;
    @Autowired
    private RedissonClient redissonClient;
    @Autowired
    @Lazy
    private BloomFilterTool self;

    @Scheduled(fixedDelay = SCHEDULER_PERIOD, initialDelay = SCHEDULER_PERIOD)
    public void scheduleTryInitBloomFilter() {
        log.info("[scheduleTryInitBloomFilter]");
        self.tryInitBloomFilter();
    }

    @PostConstruct
    public void tryInitBloomFilter() {
        String[] currentKeyAndPreKey = getCurrentMinutesKeyAndPreKey();
        String currentKey = currentKeyAndPreKey[0];
        String preKey = currentKeyAndPreKey[1];
        if (currentKey.equals(lastBloomFilterKey)) {
            //已经初始化，跳过
            return;
        }
        try {
            //初始化当前时间需要的BloomFilter,并清理旧BloomFilter
            log.info("[tryInitBloomFilter][key:{}]", currentKey);
            RBloomFilter<String> bloomFilter = redissonClient.getBloomFilter(currentKey);
            //初始化布隆过滤器：预计元素为100000000L,误差率为0.0001%
            bloomFilter.tryInit(expectedInsertions, falseProbability);
            lastBloomFilterKey = currentKey;
            delRBloomFilterByKey(preKey);
        } catch (Exception e) {
            log.error("[tryInitBloomFilter] {}", e.getMessage());
        }
    }

    public boolean containWordAndAdd(String word) {
        try {
            if (lastBloomFilterKey == null) {
                return false;
            }
            RBloomFilter<String> bloomFilter = redissonClient.getBloomFilter(lastBloomFilterKey);
            return !bloomFilter.add(word);
        } catch (Exception e) {
            log.error("bloomFilter containWordAndAdd:{}", e.getMessage());
            tryInitBloomFilter();
            return false;
        }
    }

    private void delRBloomFilterByKey(String delkey) {
        log.info("[try del BloomFilter][key:{}]", delkey);
        RBloomFilter<String> delbBloomFilter = redissonClient.getBloomFilter(delkey);
        boolean exists = delbBloomFilter.isExists();
        if (exists) {
            log.info("[try del BloomFilter][key:{}]", delkey);
            delbBloomFilter.delete();
        }
    }

    private String[] getCurrentMinutesKeyAndPreKey() {
        LocalDateTime nowData = LocalDateTime.now();
        String localDataStr = nowData.format(DateTimeFormatter.ofPattern("yyyyMMddHHmm"));
        String currentKey = BloomFilterKeyPrefix + localDataStr;

        LocalDateTime yesterdayLocalDate = nowData.minusMinutes(1);
        String yesterdayDataStr = yesterdayLocalDate.format(DateTimeFormatter.ofPattern("yyyyMMddHHmm"));
        String yesterdayKey = BloomFilterKeyPrefix + yesterdayDataStr;
        return new String[]{currentKey, yesterdayKey};
    }

    private String[] getCurrentDayKeyAndPreKey() {
        DateTimeFormatter yyyyMMdd = DateTimeFormatter.ofPattern("yyyyMMdd");
        LocalDate nowData = LocalDate.now();
        String localDataStr = yyyyMMdd.format(nowData);
        String currentKey = BloomFilterKeyPrefix + localDataStr;
        LocalDate yesterdayLocalDate = nowData.minusDays(1);
        String yesterdayDataStr = yyyyMMdd.format(yesterdayLocalDate);
        String yesterdayKey = BloomFilterKeyPrefix + yesterdayDataStr;
        return new String[]{currentKey, yesterdayKey};
    }
}
